package info.batcloud.fanli.api.security.wexin.authentication;

import info.batcloud.fanli.api.constants.Constants;
import info.batcloud.fanli.api.exception.WeixinNeitherRegisterNorBindException;
import info.batcloud.fanli.api.security.handler.LoginAuthenticationFailureHandler;
import info.batcloud.fanli.api.security.handler.LoginAuthenticationSuccessHandler;
import info.batcloud.fanli.core.domain.DefaultUserDetails;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.enums.Gender;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.security.authentication.NoopAuthenticationManager;
import info.batcloud.fanli.core.service.SystemSettingService;
import info.batcloud.fanli.core.service.UserService;
import info.batcloud.fanli.core.service.WeixinService;
import info.batcloud.fanli.core.settings.WeixinSetting;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import static info.batcloud.fanli.api.constants.Constants.STATE_INVITATION;

@Component
public class WeixinAuthenticationAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    @Inject
    private UserRepository userRepository;

    @Inject
    private UserService userService;

    @Inject
    private LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;

    @Inject
    private LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;

    @Inject
    private SystemSettingService systemSettingService;

    @Inject
    private WeixinService weixinService;

    public WeixinAuthenticationAuthenticationProcessingFilter() {
        super("/login/weixin");
        setAuthenticationManager(new NoopAuthenticationManager());
        setAuthenticationDetailsSource(authenticationDetailsSource);
    }

    @PostConstruct
    public void afterPropertiesSet() {
        this.setAuthenticationSuccessHandler(loginAuthenticationSuccessHandler);
        this.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {

        String code = request.getParameter("code");
        String state = StringUtils.defaultIfEmpty(request.getParameter("state"), "");
        WeixinSetting weixinSetting = systemSettingService.findActiveSetting(WeixinSetting.class);
        String appId, secret;
        switch (state) {
            case STATE_INVITATION:
                appId = weixinSetting.getGzhAppId();
                secret = weixinSetting.getGzhSecret();
                break;
            default:
                appId = weixinSetting.getAppId();
                secret = weixinSetting.getSecret();
        }

        WeixinService.AccessToken accessToken = weixinService.auth(code, appId,
                secret);
        String unionId = accessToken.getUnionid();
        DefaultUserDetails userDetails = null;
        if (StringUtils.isNotEmpty(unionId)) {
            userDetails = userService.loadUserByWeixinUnionId(accessToken.getUnionid());
        }
        if (userDetails == null) {
            userDetails = userService.loadUserByWeixinOpenId(accessToken.getOpenid());
        }
        User user;
        if (userDetails == null) {
            if (state.equals(STATE_INVITATION)) {
                String invitationCode = (String) request.getSession().getAttribute(Constants.INVITATION_CODE);
                UserService.WeixinUserRegisterParam param = new UserService.WeixinUserRegisterParam();
                param.setInvitationCode(invitationCode);
                param.setWeixinBindOpenId(accessToken.getOpenid());
                param.setUnionId(accessToken.getUnionid());
                UserService.RegisterResult rs = userService.registerUser(param);
                if (!rs.isSuccess()) {
                    throw new AuthenticationServiceException(rs.getErrMsg());
                }
                user = userRepository.findByWeixinUnionIdAndDeleted(accessToken.getUnionid(), false);
            } else {
                throw new WeixinNeitherRegisterNorBindException();
            }
        } else {
            user = userRepository.findOne(userDetails.getUserId());
            user.setWeixinUnionId(accessToken.getUnionid());
        }
        if (user.getSuperUser() == null) {
            String invitationCode = (String) request.getSession().getAttribute(Constants.INVITATION_CODE);
            if (StringUtils.isNotEmpty(invitationCode)) {
                user.setSuperUser(userRepository.findByInvitationCode(invitationCode));
            }
        }
        if (user.getSuperUser() == null) {
            user.setLocked(true);
        }
        WeixinService.UserInfo ui = weixinService.userInfo(accessToken.getAccessToken(), accessToken.getOpenid());
        user.setNickname(ui.getNickname());
        user.setAvatarUrl(ui.getHeadimgurl());
        user.setGender(ui.getSex() == 1 ? Gender.MALE : Gender.FAMALE);
        userRepository.save(user);
        if (userDetails == null) {
            userDetails = userService.loadUserByWeixinUnionId(unionId);
        }
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }


}
